package com.user.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.user.service.UserDaoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDaoService userService;


    @Bean
    PasswordEncoder passwordEncoder(){
        // 使用BCrypt强哈希函数加密方案，密钥迭代次数设为10（默认即为10）
        return new BCryptPasswordEncoder(10);
    }

    // 配置用户及其对应的角色  调用service 验证用户情况
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }


    // 配置 URL 访问权限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                //当发现login时认为是登录需要执行我们自定义的登录逻辑 >里面的url是登录页面表单的提交地址
                .loginProcessingUrl("/login")
                //登录成功后请求地址 请求方法必须是post的
                .successForwardUrl("/toMain")
                //设置登录页面
                .loginPage("http://localhost:8080/");

        //url拦截认证  >所有请求都必须被认证 必须登录后才可以访问
        http.authorizeRequests() // 开启 HttpSecurity 配置
                .antMatchers("/db/**").hasRole("DBA") // db/** 模式URL需DBA角色
                .antMatchers("/admin/**").hasRole("ADMIN") // admin/** 模式URL需ADMIN角色
                .antMatchers("/user/**").hasRole("USER") // user/** 模式URL需USER角色
                .anyRequest().authenticated() // 用户访问其它URL都必须认证后访问（登录后访问）
                .and().formLogin().loginProcessingUrl("/login").permitAll() // 开启表单登录并配置登录接口
                .and().logout() // 开启注销登录的配置
                .logoutUrl("/logout") // 配置注销登录请求URL为"/logout"（默认也就是 /logout）
                .clearAuthentication(true) // 清除身份认证信息
                .invalidateHttpSession(true) // 使 session 失效
                // 配置一个 LogoutHandler，开发者可以在这里完成一些数据清除工做
                .addLogoutHandler(new LogoutHandler() {
                    @Override
                    public void logout(HttpServletRequest req,
                                       HttpServletResponse resp,
                                       Authentication auth) {
                        System.out.println("注销登录，开始清除Cookie。");
                    }
                })
                // 配置一个 LogoutSuccessHandler，开发者可以在这里处理注销成功后的业务逻辑
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                Authentication auth)
                            throws IOException, ServletException {
                        // 我们可以跳转到登录页面
                        // resp.sendRedirect("/login");

                        // 也可以返回一段JSON提示
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        resp.setStatus(200);
                        Map<String, Object> map = new HashMap<>();
                        map.put("status", 200);
                        map.put("msg", "注销成功！");
                        ObjectMapper om = new ObjectMapper();
                        out.write(om.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and().csrf().disable(); // 关闭csrf
    }

    @Bean
    public CustomFilterInvocationSecurityMetadataSource accessMustRoles() {
        return new CustomFilterInvocationSecurityMetadataSource();
    }

    @Bean
    public CustomAccessDecisionManager rolesCheck() {
        return new CustomAccessDecisionManager();
    }

    // 配置角色继承关系
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_DBA > ROLE_ADMIN > ROLE_USER";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
}
